package cn.bluethink.eguan.rewardtask.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.github.pagehelper.PageHelper;

import cn.bluethink.eguan.core.service.EgAcountService;
import cn.bluethink.eguan.core.service.EgBtsService;
import cn.bluethink.eguan.core.service.EgFileService;
import cn.bluethink.eguan.core.service.EgResultService;
import cn.bluethink.eguan.core.service.EgTavernService;
import cn.bluethink.eguan.model.core.EgFile;
import cn.bluethink.eguan.model.core.EgOType;
import cn.bluethink.eguan.model.core.EgResult;
import cn.bluethink.eguan.model.core.EgTaskStatus;
import cn.bluethink.eguan.model.rewardtask.EgReceiver;
import cn.bluethink.eguan.model.rewardtask.EgRewardTask;
import cn.bluethink.eguan.result.PageInfo;
import cn.bluethink.eguan.rewardtask.entity.EgReceiverEntity;
import cn.bluethink.eguan.rewardtask.entity.ReceiverEntity;
import cn.bluethink.eguan.rewardtask.filter.ReceiverFilter;
import cn.bluethink.eguan.rewardtask.mapper.EgReceiverMapper;
import cn.bluethink.eguan.utils.IdMakerUtils;
import tk.mybatis.mapper.entity.Example;

/**
 * 悬赏任务揭榜者控制器
 * 
 * @author 苏州中科蓝迪公司所有(c)2016-2021
 * @version 0.2.0
 * @brief 驿馆+产品
 * @note 修订历史： 1.wanglei于2018/10/18设计并构建初始版本v0.2.0
 *       2.wanglei于2018/11/8优化过滤查询方式为拼接SQL 3.wanglei于2018/11/15重新修订打赏时的账户处理逻辑
 */
@Service
public class EgReceiverService {

    private IdMakerUtils idUtil = new IdMakerUtils();

    @Autowired
    private EgReceiverMapper receiverMapper;

    @Autowired
    private EgRewardTaskService rewardTaskService;

    @Autowired
    private EgAcountService acountService;

    @Autowired
    private EgBtsService btsService;

    @Autowired
    private EgTavernService tavernService;

    @Autowired
    private EgFileService fileService;

    @Autowired
    private EgResultService resultService;

    /**
     * 保存领取者(被指派者)信息,区分状态
     * 
     * @param reward
     * @param uid
     * @param status
     * @return
     * @throws Exception
     */
    @Transactional(transactionManager = "eguanTransactionManager", rollbackFor = Exception.class)
    public EgReceiver saveReceiver(EgRewardTask reward, Long uid, Integer status) throws Exception {
        // 增加悬赏任务的揭榜数量
        reward.setRvcount(reward.getRvcount() + 1);
        if (rewardTaskService.updateRewardTask(reward) == null) {
            throw new RuntimeException("领取/指派失败，请重试！");
        }
        // 保存领取者的信息(除了任务完成时间，完成结果)默认领取的赏金为0
        EgReceiverEntity entity = new EgReceiverEntity(idUtil.nextId(), uid, reward.getId(), status, 0);
        // 如果状态为【进行中】,需要设置开始时间
        if (status.equals(EgTaskStatus.STATUS_HANDLE)) {
            entity.setBtime(new Date());
        }
        if (receiverMapper.insertSelective(entity) == 0) {
            throw new RuntimeException("领取/指派失败，请重试！");
        }
        return entity.createEgReceiver();
    }

    /**
     * 移除悬赏任务的被指派者
     * 
     * @param tid
     * @param ruid
     * @param receiver
     * @return
     * @throws Exception
     */
    @Transactional(transactionManager = "eguanTransactionManager", rollbackFor = Exception.class)
    public Boolean remove(Long tid, Long ruid, EgReceiver receiver) throws Exception {
        // 获取悬赏任务的特有属性
        EgRewardTask reward = rewardTaskService.queryById(tid);
        if (reward == null) {
            return false;
        }
        // 减少悬赏任务的揭榜数量
        reward.setRvcount(reward.getRvcount() - 1);
        if (rewardTaskService.updateRewardTask(reward) == null) {
            throw new RuntimeException("移除/放弃失败，请重试！");
        }
        Example example = new Example(EgReceiverEntity.class);
        example.and().andEqualTo("tid", tid).andEqualTo("uid", ruid);
        if (receiverMapper.deleteByExample(example) == 0) {
            throw new RuntimeException("移除/放弃失败，请重试！");
        }
        // 如果是放弃悬赏任务(有提交结果的需要删除结果)
        if (receiver != null) {
            // 获取提交的结果对象
            EgResult result = receiver.getResult();
            if (result != null && result.getId() != null && result.getId() > 0) {
                if (!resultService.delete(result.getId(), true)) {
                    throw new RuntimeException("移除/放弃失败，请重试！");
                }
            }
        }
        return true;
    }

    /**
     * 更新领取者基本信息
     * 
     * @param receiver
     * @param flag
     * @return
     * @throws Exception
     */
    @Transactional(transactionManager = "eguanTransactionManager", rollbackFor = Exception.class)
    public EgReceiver update(EgReceiver receiver, Boolean flag) throws Exception {
        EgReceiverEntity entity = new EgReceiverEntity(receiver);
        int updateCount = 0;
        if (flag) {
            // 根据主键更新属性不为null的值
            updateCount = receiverMapper.updateByPrimaryKeySelective(entity);
        } else {
            // 根据主键更新实体全部字段，null值会被更新
            updateCount = receiverMapper.updateByPrimaryKey(entity);
        }
        return updateCount > 0 ? entity.createEgReceiver() : null;
    }

    /**
     * 更新领取者的状态
     * 
     * @param oldReceiver
     * @param upReceiver
     * @param rewardTask
     * @return
     * @throws Exception
     */
    @Transactional(transactionManager = "eguanTransactionManager", rollbackFor = Exception.class)
    public EgReceiver updateStatus(EgReceiver oldReceiver, EgReceiver upReceiver, EgRewardTask rewardTask)
            throws Exception {
        // 获取领取者的原来状态以及待更新的状态
        Integer oldStatus = oldReceiver.getStatus();
        Integer upStatus = upReceiver.getStatus();
        // 若更新的状态已存在，直接返回
        if (upStatus != null && oldStatus.equals(upStatus)) {
            return oldReceiver;
        }
        EgReceiver result = null;
        // 参与类型为指派,说明被指派者的状态是【未开始】
        if (oldStatus.equals(EgTaskStatus.STATUS_NEW)) {
            // 更新状态【未开始】→【进行中】以及开始时间
            if (upStatus != null && upStatus.equals(EgTaskStatus.STATUS_HANDLE)) {
                oldReceiver.setStatus(EgTaskStatus.STATUS_HANDLE);
                oldReceiver.setBtime(new Date());
                result = update(oldReceiver, true);
            }
        }
        // 参与类型为指派或其他,任务完成时，设置状态【完成】以及完成时间
        if (upStatus != null && upStatus.equals(EgTaskStatus.STATUS_FINISH)) {
            // 如果揭榜者没有提交结果(结果描述和附件)，直接点击完成的，将完成时间作为提交时间
            if (oldReceiver.getResult() == null) {
                oldReceiver.setSubTime(new Date());
            }
            oldReceiver.setStatus(EgTaskStatus.STATUS_FINISH);
            oldReceiver.setEtime(new Date());
            result = update(oldReceiver, true);
        }
        return result != null ? result : null;
    }

    /**
     * 打赏领取者
     * 
     * @param rewardTask
     * @param reward
     * @param receiver
     * @param bt
     * @return
     * @throws Exception
     */
    @Transactional(transactionManager = "eguanTransactionManager", rollbackFor = Exception.class)
    public Boolean reward(EgRewardTask rewardTask, EgRewardTask reward, EgReceiver receiver, Integer bt)
            throws Exception {
        // 悬赏任务的揭榜者金币累计数目增加bt
        reward.setRvBtCoin(reward.getRvBtCoin() + bt);
        if (rewardTaskService.updateRewardTask(reward) == null) {
            throw new RuntimeException("打赏失败，请重试！");
        }
        // 揭榜者赏金相继增加bt
        receiver.setReward(receiver.getReward() + bt);
        if (update(receiver, true) == null) {
            throw new RuntimeException("打赏失败，请重试！");
        }
        // 根据任务ID,用户ID获取相应的账户ID
        Map<String, Long> acountMap = rewardTaskService.getAcountId(reward.getId(), receiver.getUser().getId());
        if (acountMap == null || acountMap.size() <= 0) {
            return false;
        }
        Long taskAcount = acountMap.get("taskAcount");
        Long userAcount = acountMap.get("userAcount");
        // 对该任务引用的账户扣除相应的金币
        if (!acountService.updateAcount(taskAcount, bt, false)) {
            throw new RuntimeException("打赏失败，请重试！");
        }
        // 对该揭榜者的账户增加相应的金币
        if (!acountService.updateAcount(userAcount, bt, true)) {
            throw new RuntimeException("打赏失败，请重试！");
        }
        // 记录金币流通
        if (btsService.create(taskAcount, rewardTask, bt, null, userAcount) == null) {
            throw new RuntimeException("打赏失败，请重试！");
        }
        return true;
    }

    /**
     * 根据悬赏任务的ID和接收者的状态查询接收者的数目
     * 
     * @param tid
     * @param status
     * @return
     * @throws Exception
     */
    public Integer getReceiversCount(Long tid, Integer status) throws Exception {
        Example example = new Example(EgReceiverEntity.class);
        example.and().andEqualTo("tid", tid).andEqualTo("status", status);
        int selectCount = receiverMapper.selectCountByExample(example);
        return selectCount > 0 ? selectCount : 0;
    }

    /**
     * 根据悬赏任务的ID删除领取者列表
     * 
     * @param tid
     * @return
     * @throws Exception
     */
    @Transactional(transactionManager = "eguanTransactionManager", rollbackFor = Exception.class)
    public Integer delReceiversById(Long tid) throws Exception {
        Example example = new Example(EgReceiverEntity.class);
        example.and().andEqualTo("tid", tid);
        int delCount = receiverMapper.deleteByExample(example);
        return delCount > 0 ? delCount : 0;
    }

    /**
     * 根据悬赏任务的ID和揭榜者ID获取揭榜者详情
     * 
     * @param tid
     * @param ruid
     * @return
     * @throws Exception
     */
    public EgReceiver getById(Long tid, Long ruid) throws Exception {
        Example example = new Example(EgReceiverEntity.class);
        example.and().andEqualTo("tid", tid).andEqualTo("uid", ruid);
        EgReceiverEntity entity = receiverMapper.selectOneByExample(example);
        return entity != null ? entity.createEgReceiver() : null;
    }

    /**
     * 提交结果
     * 
     * @param result
     * @param receiver
     * @return
     * @throws Exception
     */
    @Transactional(transactionManager = "eguanTransactionManager", rollbackFor = Exception.class)
    public EgResult submitResult(EgResult result, EgReceiver receiver) throws Exception {
        // 如果有提交结果的需要删除结果
        EgResult resultObj = receiver.getResult();
        if (resultObj != null && resultObj.getId() != null && resultObj.getId() > 0) {
            if (!resultService.delete(resultObj.getId(), true)) {
                throw new RuntimeException("提交结果失败，请重试！");
            }
        }
        List<EgFile> subFiles = new ArrayList<>();
        // 如果提交的结果附件不为空
        List<EgFile> fileList = result.getFileList();
        if (fileList != null && fileList.size() > 0) {
            // 批量提交文件
            subFiles = fileService.createList(fileList);
            if (subFiles == null) {
                throw new RuntimeException("提交结果失败，请重试！");
            }
        }
        // 创建新的结果
        result = resultService.create(result);
        if (result == null) {
            throw new RuntimeException("提交结果失败，请重试！");
        }
        result.setType(new EgOType(EgOType.RECEIVER, null));
        result.setFileList(subFiles);
        // 提交成果，获取新的结果对象ID以及提交时间
        EgResult newResult = new EgResult();
        newResult.setId(result.getId());
        receiver.setResult(newResult);
        receiver.setSubTime(new Date());
        if (update(receiver, true) == null) {
            throw new RuntimeException("提交结果失败，请重试！");
        }
        return result;
    }

    /**
     * 删除提交的文件列表（单个删除）
     * 
     * @param fileId
     * @param receiver
     * @param result
     * @return
     * @throws Exception
     */
    @Transactional(transactionManager = "eguanTransactionManager", rollbackFor = Exception.class)
    public Boolean delResult(Long fileId, EgReceiver receiver, EgResult result) throws Exception {
        // 删除指定的文件
        if (!fileService.del(fileId)) {
            throw new RuntimeException("删除结果失败，请重试！");
        }
        // 获取删除后的文件列表对象
        List<Long> uids = Arrays.asList(new Long[] { receiver.getUser().getId() });
        List<EgFile> files = fileService.queryByUploaders(receiver.getTask().getId(), EgOType.REWARD_TASK, uids);
        if (files != null && files.size() > 0) {
            // 将删除文件后剩的文件列表更新到结果表
            result.setFileList(files);
            if (resultService.update(result, true) == null) {
                throw new RuntimeException("删除结果失败，请重试！");
            }
        } else {
            // 文件全部删除，判断提交的结果是否包含了结果描述
            if (result.getDes() == null || "".equals(result.getDes())) {
                receiver.setResult(null);
                receiver.setSubTime(null);
                // 根据主键更新实体全部字段，null值会被更新
                if (update(receiver, false) == null) {
                    throw new RuntimeException("删除结果失败，请重试！");
                }
                // 删除结果表(此时文件已经删除了，无需删除了)
                if (!resultService.delete(result.getId(), false)) {
                    throw new RuntimeException("删除结果失败，请重试！");
                }
            } else {
                // 否则只更新文件列表为空
                result.setFileList(null);
                if (resultService.update(result, false) == null) {
                    throw new RuntimeException("删除结果失败，请重试！");
                }
            }
        }
        return true;
    }

    /**
     * 根据过滤条件查询悬赏任务接收者列表。 1.根据悬赏任务ID查询揭榜者列表：
     * （1）需要提交任务结果的悬赏任务，展示进行中和已完成的揭榜者，按照揭榜者提交结果的时间降序排序；
     * （2）无需提交任务结果的悬赏任务，只展示已完成的揭榜者，按照揭榜者完成时间降序排序；
     * 2.根据当前登录用户查询我领取的悬赏任务（查询领取人列表，返回信息包括悬赏任务基本信息（含位置信息）），可根据状态筛选，根据领取时间由近及远排序。
     * 
     * @param filter
     * @param uid
     * @return
     * @throws Exception
     */
    public PageInfo<EgReceiver> query(ReceiverFilter filter, Long uid) throws Exception {
        Integer pageNum = filter.getPageNum() == null ? 1 : filter.getPageNum();
        Integer pageSize = filter.getPageSize() == null ? 50 : filter.getPageSize();
        /*
         * 声明过滤条件(包括：查询当前用户创建的逻辑表示/对象ID/对象ID是否为创建者ID的逻辑标识)
         */
        Boolean bmine = filter.getBmine() == null ? false : filter.getBmine();
        Long id = filter.getId() == null ? 0 : filter.getId();
        Boolean buid = filter.getBuid() == null ? false : filter.getBuid();
        // 构造查询条件
        String whereSql = buildQuerySql(filter, uid);
        // 返回结果
        List<EgReceiver> result = new ArrayList<>();
        List<ReceiverEntity> entities = new ArrayList<>();
        // 判断whereSql不为空，根据条件查询
        if (StringUtils.isNotBlank(whereSql)) {
            // 分页
            PageHelper.startPage(pageNum, pageSize);
            entities = receiverMapper.selectReceiversByCondition(whereSql);
        }
        PageInfo<ReceiverEntity> oldPageInfo = new PageInfo<>(entities);
        if (entities != null && entities.size() > 0) {
            result = entities.stream().map(e -> e.createReceiver()).collect(Collectors.toList());
            // 加载悬赏任务信息(查询我领取的悬赏任务时加载)
            if (bmine) {
                loadRewadTask(result);
            }
            if (id > 0 && !buid) {
                result.forEach(r -> r.setTask(null));
                // 加载提交的结果
                loadSubmitResult(id, result);
            }
        }
        return new PageInfo<>(result, oldPageInfo);
    }

    /**
     * 构建query查询条件
     * 
     * @param filter
     * @param uid
     * @return
     * @throws Exception
     */
    private String buildQuerySql(ReceiverFilter filter, Long uid) throws Exception {
        String whereSql = "";
        List<String> whereSqlList = new ArrayList<>();
        /*
         * 声明过滤条件(包括：查询当前用户创建的逻辑表示/对象ID/对象ID是否为创建者ID的逻辑标识/任务的状态)
         */
        Boolean bmine = filter.getBmine() == null ? false : filter.getBmine();
        Long id = filter.getId() == null ? 0 : filter.getId();
        Boolean buid = filter.getBuid() == null ? false : filter.getBuid();
        Integer status = filter.getStatus();
        // 获取门派ID
        Long fid = filter.getFid() == null ? 0 : filter.getFid();
        // 前提是悬赏任务没有被逻辑删除
        whereSqlList.add(" T.del_flag = 0");
        // 根据领取者id进行查询
        if (bmine) {
            whereSqlList.add(" R.uid = " + uid);
            // 默认查询所有的；如果门派ID不为空，查询该门派(以及门派下所有驿馆)下发布的悬赏任务;
            if (fid > 0) {
                List<Long> tavernIds = tavernService.egidsByFid(fid);
                if (tavernIds != null && tavernIds.size() > 0) {
                    whereSqlList.add(
                            " (T.pos_oid = " + fid + " OR T.pos_oid IN (" + StringUtils.join(tavernIds, ",") + "))");
                }
            }
        }
        // 对象ID不为0且为任务ID。
        if (id > 0 && !buid) {
            whereSqlList.add(" R.tid = " + id);
        }
        // 按照领取者状态排序
        if (status != null) {
            whereSqlList.add(" R.status= " + status);
        }
        // 设置查询条件AND连接符
        if (whereSqlList.size() > 0) {
            whereSql += String.join(" AND ", whereSqlList);
        }
        whereSql += " ORDER BY ";
        if (bmine) {
            // 根据领取者id进行查询时，按照领取时间降序排序
            whereSql += " R.btime Desc ";
        }
        if (id > 0 && !buid) {
            whereSql += " R.sub_time Desc ";
        }
        return whereSql;
    }

    /**
     * 加载悬赏任务的详情信息
     * 
     * @param receivers
     * @throws Exception
     */
    private void loadRewadTask(List<EgReceiver> receivers) throws Exception {
        // 获取任务的ID列表
        List<Long> tids = receivers.stream().map(r -> r.getTask().getId()).distinct().collect(Collectors.toList());
        if (tids != null && tids.size() > 0) {
            // 获取悬赏任务列表
            List<EgRewardTask> rewardTasks = rewardTaskService.queryByIds(tids);
            if (rewardTasks != null && rewardTasks.size() > 0) {
                // 键值对方式存储上述查询的悬赏任务
                Map<Long, EgRewardTask> rewardTaskMap = new HashMap<>();
                rewardTasks.forEach(r -> rewardTaskMap.put(r.getId(), r));
                for (EgReceiver receiver : receivers) {
                    // 获取任务ID
                    Long tid = receiver.getTask().getId();
                    EgRewardTask rewardTask = rewardTaskMap.get(tid);
                    if (rewardTask != null) {
                        receiver.setTask(rewardTask);
                    }
                }
            }
        }
    }

    /**
     * 加载提交的结果
     * 
     * @param tid
     * @param receivers
     * @throws Exception
     */
    private void loadSubmitResult(Long tid, List<EgReceiver> receivers) throws Exception {
        // 获取所有揭榜者的ID列表
        List<Long> rIds = receivers.stream().map(r -> r.getUser().getId()).collect(Collectors.toList());
        if (rIds != null && rIds.size() > 0) {
            // 获取所有的提交结果对象列表(包含提交的文件信息)
            List<EgResult> results = resultService.query(tid, EgOType.REWARD_TASK, rIds);
            Map<Long, EgResult> resultMap = new HashMap<>();
            if (results != null && results.size() > 0) {
                results.forEach(r -> resultMap.put(r.getUser().getId(), r));
            }
            if (resultMap != null && resultMap.size() > 0) {
                for (EgReceiver receiver : receivers) {
                    // 获取揭榜者ID
                    Long rId = receiver.getUser().getId();
                    EgResult result = resultMap.get(rId);
                    if (result != null) {
                        result.setType(new EgOType(EgOType.RECEIVER, null));
                        receiver.setResult(result);
                    }
                }
            }
        }
    }
}
